home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 429_01 / kbfake10 / kbfake.c < prev    next >
C/C++ Source or Header  |  1994-04-06  |  7KB  |  205 lines

  1. /*                KBFAKE.C
  2.  
  3.     This program is a Terminate and Stay Resident program that will
  4.   redirect the input from one serial line so that it looks like it has
  5.   been typed on the keyboard.  It works by installing an interrupt handler
  6.   for the serial port that is to be used.  This handler inserts the
  7.   characters coming in over the serial line into the keyboard buffer using
  8.   the BIOS keyboard write call.
  9.     The code for this program is a mixture of the example for the
  10.   keep() function call in Borland C and various programming examples from
  11.   the SIMTEL internet FTP site.
  12.     This is an interrupt service routine.  You can NOT compile this
  13.   program with Test Stack Overflow turned on and get an executable file
  14.   which will operate correctly. */
  15.  
  16. #include    <stdlib.h>
  17. #include    <stdio.h>
  18. #include    <bios.h>    // bioscom() declared here
  19. #include    <dos.h>        // keep() is declared here
  20.  
  21. // Reduce heaplength and stacklength to make a smaller program in memory
  22. extern unsigned _heaplen = 256;
  23. extern unsigned _stklen  = 1024;
  24.  
  25. const    unsigned    BIOSdseg = 0x0040;    // BIOS data segment
  26. const    unsigned    IMR = 0x21;        // Interrupt Mask Register
  27. const    unsigned    safety_space = 1024;    // Bytes of spare space
  28.  
  29. // These are offsets from the UART base port
  30. const    unsigned    UART_THR = 0x00;    // Transmit Hold Register and
  31. const    unsigned    UART_RBR = 0x00;    // Receiver Buffer Register
  32. const    unsigned    UART_IER = 0x01;    // Interrupt Enable Register
  33. const    unsigned    UART_IIR = 0x02;
  34. const    unsigned    UART_LCR = 0x03;    // Line Control Register?
  35. const    unsigned    UART_MCR = 0x04;    // Modem Control Register?
  36. const    unsigned    UART_LSR = 0x05;    // Line Status Register
  37. const    unsigned    UART_MSR = 0x06;
  38.  
  39. //-----------------------------------------------------------------
  40. // Global variables needed both during initialization and interrupt
  41. // processing.
  42. //-----------------------------------------------------------------
  43.  
  44. int    base_port;    // Base I/O port address
  45.  
  46.  
  47. //-----------------------------------------------------------------
  48. //    Handle the incoming data interrupt from the serial port.
  49. // Read the incoming character and add it to the BIOS keyboard
  50. // buffer.
  51. //    Note: Interrupts are disabled by the processor as part of
  52. // the interrupt processing, so we do not need to disable them by
  53. // hand.  We also don't need to enable interrupts before leaving,
  54. // since the FLAGS register is popped from the stack upon IRET,
  55. // restoring the interrupt control flag.
  56. //-----------------------------------------------------------------
  57.  
  58. void interrupt serial_input_handler(void) {
  59.     static    unsigned char    in_char;
  60.     static    unsigned char    scancode;
  61.  
  62.     // Get the character from the comm port RBR register
  63.     in_char = inportb(base_port + UART_RBR);
  64.  
  65.     // Set the keyboard scan code to zero, which is what would
  66.     // happen if they had used the alt-numpad method to enter
  67.     // the character, rather than pressing the key.  This should
  68.     // not be noticed by keyboard-reading software, and saves
  69.     // having to keep a table of key scan codes for each ASCII
  70.     // code.
  71.     scancode = 0;
  72.  
  73.     // Write the character to the keyboard buffer using BIOS
  74.     // Use BIOS call 0x16 (Keyboard services)
  75.     //    AH = service to use = 0x05 (keyboard write)
  76.     //    CL = ASCII code of character
  77.     //    CH = Keyboard scan code of character
  78.     _AH = 0x05;
  79.     _CL = in_char;
  80.     _CH = scancode;
  81.     geninterrupt(0x16);
  82.  
  83.     // Tell the 8259A End Of Interrupt
  84.     outportb(0x20,0x20);
  85. }
  86.  
  87. void    Usage(char *s) {
  88.     fprintf(stderr,"Usage: %s [port]\n",s);
  89.     fprintf(stderr,"       port: Serial COMM port to read (1-4)\n");
  90.     fprintf(stderr,"             (default = 1)\n");
  91.     exit(-1);
  92. }
  93.  
  94. void    main(unsigned argc, char *argv[]) {
  95.     int    port = 1;    // COMM port to read (1-4)
  96.     int    temp;               // Temporary holding register
  97.     int    int_to_grab;    // Which interrupt vector to grab
  98.     int    int_to_enable;    // Which hardware interrupt to enable
  99.  
  100.     // Parse the command line
  101.     switch (argc) {
  102.       case 1:    // No arguments
  103.         break;
  104.       case 2:    // COM port number (1-4) as argument
  105.         port = atoi(argv[1]);
  106.         if ( (port < 1) || (port > 4) ) Usage(argv[0]);
  107.         break;
  108.       default:
  109.         Usage(argv[0]);
  110.     }
  111.  
  112.     // Find the interrupt to use based on the port selected
  113.     // For some reason, the interrupt you grab the vector for is
  114.     // not the same as the hardware interrupt you enable.
  115.     switch (port) {
  116.       case 1:
  117.       case 3:
  118.         int_to_grab = 0x0c;
  119.         int_to_enable = 4;
  120.         break;
  121.       case 2:
  122.       case 4:
  123.         int_to_grab = 0x0b;
  124.         int_to_enable = 3;
  125.         break;
  126.       default:
  127.         fprintf(stderr,"Internal error 1 - port is %d\n",port);
  128.         exit(-1);
  129.     }
  130.  
  131.     // Find the I/O base address based on the port selected
  132.     // This information is read from the BIOS data segment that
  133.     // is at segment 0x0040.  The first four words hold the
  134.     // I/O base addresses for the first four com ports.
  135.     base_port = *(int far *)MK_FP(BIOSdseg,(port-1)*2);
  136.     if (base_port == 0) {
  137.         fprintf(stderr,"COM%d has no BIOS base port\n",port);
  138.         fprintf(stderr,"    (KBFAKE not installed)\n");
  139.         exit(-1);
  140.     }
  141.  
  142.     // Tell what we're doing
  143.     printf("KBFAKE: Installing on port %d (vector %2X, interrupt %d, I/O base %4X)\n",
  144.         port, int_to_grab, int_to_enable, base_port);
  145.  
  146.     // Set the parameters on the COM port to:
  147.     //    0xE0:    9600 Baud
  148.     //    0x03:    8 Data bits    (0x02 = 7 data bits)
  149.     //    0x00:    1 Stop bit    (0x04 = 2 stop bits)
  150.     //    0x00:    No parity    (0x08 = odd, 0x18 = even)
  151.     (void)bioscom(0, 0xE0 | 0x03 | 0x00 | 0x00 , port-1);
  152.     printf("        9600 baud, 8 data bits, 1 stop bit, no parity\n");
  153.  
  154.     /* install the new interrupt handler for serial input */
  155.     setvect(int_to_grab, serial_input_handler);
  156.  
  157.     // Disable interrupts while we are messing with the UART
  158.     disable();
  159.  
  160.     //-----------------------------------------------------------------
  161.     // Program the UART for the serial port we are using
  162.     //-----------------------------------------------------------------
  163.  
  164.     // Turn off the Divisor Access Latch Bit to allow access to RBR, etc.
  165.     temp = inportb(base_port + UART_LCR) & 0x7F;  // Strip upper bit
  166.     outportb(base_port + UART_LCR, temp);
  167.  
  168.     // Assert DTR, RTS, and OUT2 on the COM port
  169.     outportb(base_port + UART_MCR, 0x0B);
  170.  
  171.     // Enable interrupts for data ready on the serial line (8250)
  172.     outportb(base_port + UART_IER, 0x01);
  173.  
  174.     // Read the Line Status Register to clear any reported errors
  175.     temp = inportb(base_port + UART_LSR);
  176.  
  177.     // Read the Receiver Buffer Register to clear any incoming character
  178.     temp = inportb(base_port + UART_RBR);
  179.  
  180.     //-----------------------------------------------------------------
  181.     // Enable the IRQ for the serial line on the 8259A interrupt control
  182.     // Do this by clearing the bit in the ISR corresponding to the
  183.     // interrupt that is to be enabled.
  184.     //-----------------------------------------------------------------
  185.  
  186.     temp = inportb(IMR);    // Read the value of the Interrupt Mask Reg
  187.     temp &= ~(1 << int_to_enable);    // Zero bit for one to enable
  188.     outportb(IMR,temp);        // Send the value back
  189.  
  190.     // Re-enable interrupts, since all is set up
  191.     enable();
  192.  
  193.     // Terminate and Stay Resident
  194.     /* _psp is the starting address of the
  195.        program in memory.  The top of the stack
  196.        is the end of the program.  Using _SS and
  197.        _SP together we can get the end of the
  198.        stack.  You may want to allow a bit of
  199.        safety space to insure that enough room
  200.        is being allocated ie:
  201.        (_SS + ((_SP + safety space)/16) - _psp)
  202.     */
  203.     keep(0,(_SS+( (_SP+safety_space)/16)-_psp));
  204. }
  205.